package org.lonetree.tc.core.id;


/**
 * A ValidatedIdentifier is an encapsulation of any identifier that can be used
 * to uniquely identify some item (for example, an IdentifiableItem). The
 * identifier is validated when it is created, and the process ensures that a
 * successfully constructed ValidatedIdentifier contains no null data, is
 * complete and is of the appropriate form for whatever its purpose may be.
 * 
 * All classes that act as identifiers should extend this class and must
 * implement the validateAndFix() method, which ensures the identifier is valid
 * (not malformed).
 */
public abstract class ValidatedIdentifier{
	
	private String identifierString;
	
	protected ValidatedIdentifier(String identifier) throws ValidationException{
		quickValidate(identifier);
		identifierString = validateAndFix(identifier);
		quickValidate(identifierString);
		this.identifierString = identifierString.trim();
	}
	
	private void quickValidate(String identifierString) throws ValidationException{
		if(identifierString == null)
			throw new ValidationException(new NullPointerException("identifier cannot be null"));
		
		if(identifierString.trim().equals(""))
			throw new ValidationException(new NullPointerException("identifier cannot be empty"));
	}
	
	protected String getIdentifierString(){
		return identifierString;
	}
	
	/**
	 * Validates the String identifier. If the identifier contains minor, fixable flaws, the
	 * value returned is an identifier with absolutely correct form. If the flaws cannot be
	 * precisely identified and fixed, a ValidationException is thrown.
	 *
	 * @param identifier the identifier to validate and possibly fix; passed value will never be null or empty
	 * @return a fixed version of the identifier, if required (and possible)
	 * @throws ValidationException if the identifier is malformed and cannot be fixed
	 */
	protected abstract String validateAndFix(String identifier) throws ValidationException;

	@Override
	public final String toString(){
		return identifierString;
	}

	@Override
	public final boolean equals(Object o){
		if(o == null)
			return false;
		
		if(o == this)
			return true;
		
		if(getClass().equals(o.getClass())){
			ValidatedIdentifier id = (ValidatedIdentifier)o;
			return getIdentifierString().equals(id.getIdentifierString());
		}
		else{
			return false;
		}
	}
	
	@Override
	public final int hashCode(){
		String full = getClass().getName() + ":" + identifierString;
		return full.hashCode();
	}
	
	/**
	 * Exception to throw when an identifier is, upon inspection, found to be
	 * malformed in any way.
	 * 
	 * The most common anticipated time for this exception to be thrown is when
	 * a ValidatedIdentifer is construced. At that time, the constructor
	 * delegates validation responsibilities to the validateAndFix() method,
	 * which should throw this exception if it cannot verify the identifier
	 * is of an appropriate form (or if it cannot confidently "fix" the passed
	 * value into an appropriate form).
	 */
	public static class ValidationException extends Exception{

		private static final long serialVersionUID = 1L;
		
		/**
		 * Construct a ValidationException with an appropriate message for why
		 * the exception is being thrown (i.e. why an identifier cannot be
		 * validated).
		 *
		 * @param message - a message describing why the identifier is malformed
		 */
		public ValidationException(String message){
			super(message);
		}
		
		/**
		 * Construct a ValidatedException that has as its underlying cause
		 * another exception.
		 *
		 * @param e - the exception that caused the need for a
		 * ValidatedException to be thrown
		 */
		public ValidationException(Exception e){
			super();
			initCause(e);
		}
		
		/**
		 * Construct a ValidatedException that has a custom message as well as
		 * an underlying exception as its cause.
		 *
		 * @param message - a message describing why the identifier is malformed
		 * @param e - the exception that caused the need for a
		 * ValidatedException to be thrown
		 */
		public ValidationException(String message, Exception e){
			super(message);
			initCause(e);
		}
	}
	
	/**
	 * Checks the length of passed String.
	 *
	 * @param str the string to check the length of
	 * @param len the length str is supposed to be
	 * @param des a one or two word description of str
	 */
	protected static final void validateLength(String str, int len, String des) throws ValidationException{
		if(str == null)
			throw new ValidationException(new NullPointerException(des + " cannot be null"));
		
		if(str.length() != len)
			throw new ValidationException(des + " must be exactly " + len + " characters long");
	}
}
